<?php
require_once("simplexml44-0_4_4/class/IsterXmlSimpleXMLImpl.php");
require_once("Image/Transform.php");
require_once("PEAR.php");
require_once("ArtistWiki.php");
require_once("Info.php");

/**
 * Class for parsing data from the Last.fm webservices at http://www.audioscrobbler.net/data/webservices/ 
 * and caches it.
 * 
 * @author Magnus Stoveland <mstoveland@gmail.com>
 * 
 */
class Lastfm 
{
    /**
     * Parses text-files with lines "text,text,text\n", 
     * Made for Plain files from http://www.audioscrobbler.net/data/webservices/
     *
     * @param string $input
     * @param int $key_pos [opotional]
     * @param int $value_pos [opotional]
     * 
     * @return array with values [$key_pos] => [$val_pos] or exploded lines if not given
     */
     
    function parseFile($file, $key_pos = "", $value_pos = "") {
        $lines = file($file);
        if(is_array($lines)) {
            foreach($lines as $line) {
                $exploded_arr = explode(",", trim($line));
                if (!empty($value_pos)) {
                    $return_array[] = trim($exploded_arr[2]);
                } else {
                    $return_array[] = $exploded_arr;
                }
            }
        } else {
            trigger_error("Unable to read file: ".$file, E_USER_WARNING);
        }
        return $return_array;
    }

    /**
     * Matches array of artists and array of top artists from a last.fm profile.
     *
     * @param array $program_artists
     * @param array $profile_artists
     * @return associative array with degree of match [$program_id] => [$score]
     */
    function match($lineup, $userArtists) {
        
        //convert $userArtists to "lookup-array"
        foreach($userArtists as $userMusic) {
            
            //USE WHEN SEVER SUPPORTS: $lookupUserArtists[mb_strtolower($userMusic->artist, "UTF-8")] = $userMusic->weight;
            $lookupUserArtists[strtolower($userMusic->artist)] = $userMusic->weight;
        }
        
        // Reset counting variables
        $i = 0; 
        $score = 0;
        foreach ($lineup as $id => $venueMusic) {
            if (!empty($venueMusic->similar)) {
                // Searches for matches in similar to $artist and profile artists
                foreach ($venueMusic->similar as $similar) {
                    $degree_of_match = $similar[0];
                    //echo $similar[2]." - ";
                    //USE WHEN SEVER SUPPORTS: $artist = mb_strtolower($similar[2], "UTF-8");
                    $artist = strtolower($similar[2]);
                    //echo $artist."<br>";
                    if (array_key_exists($artist, $lookupUserArtists)) {
                        $match = new MatchInfo();
                        $match->percentage = ceil((($degree_of_match * $lookupUserArtists[$artist])/100));
                        $match->artist = $artist;
                        $lineup[$id]->match[] = $match;
                        $score += $match->percentage;
                    }
                } 
            } else {
                Info::add("No similar artists for ".$venueMusic->artist);
            }
            $ranking[$id] = $score;
            // For the next iteration, to keep the index in the program of artist with no matches with proifile
            $score = $i; 
            $i -= 0.000001;
        }
        asort($ranking);
        return Lastfm::_merge($lineup, $ranking);
    }
    
    /**
     * Used in association with Lastfm::match() to merge match score and program.
     * @see match() 
     * 
     * @param array $program_artists
     * @param array $ranking
     * @return array with sorted program
     */
    function _merge($lineup, $ranking) {
        $keys = array_keys($ranking);
        for( $i = count($lineup)-1; $i >= 0; $i-- ) {
           $resultLineup[$keys[ $i ]] = $lineup[ $keys[ $i ] ];
        }
        return $resultLineup;
    }
    /**
     * Cache file with similar artists if needed and return array with similar artists to $artistname. 
     * 
     * @see cacheSimilarArtists()
     * @param string $artistname
     * @return array with parsed file
     */
    function similarArtists($artistname) {
        // Check cache
        $file = "./cache/".Lastfm::wsEncode($artistname);
        if (!@fopen($file, "r")) {
	        // Need to cache 
	        Info::add("Caching artist ".$artistname);
	        Lastfm::cacheSimilarArtists($artistname);
        }
        
        return Lastfm::parseFile($file);
    }
    
    function topArtists($username) {
        $file = "./cache/users/".$username;
        if (!@fopen($file, "r")) {
	        // Need to cache 
	        Info::add("Caching user ".$username);
	        Lastfm::cacheUser($username);
        } 
        $artists = Lastfm::parseFile($file);
        $max_percentage = 100;
        $min_percentage = 75;
        $max_playcount = $artists[0][1];
        $min_playcount = $artists[count($artists)-1][1];
        $spread = ($max_playcount - $min_playcount);
        if ($spread == 0) {
            $spread = 1;
        }
        $step = ($max_percentage - $min_percentage)/($spread);
        foreach($artists as $id => $artist) {
            $userMusic = new UserMusic();
            $userMusic->artist = $artist[2];
            $userMusic->weight = $min_percentage + ($artist[1]-$min_playcount) * $step;
            $out[] = $userMusic;
        }
        return $out;
    }
    
    /**
     * Checks for occurrences of "pattern" in $sting. If found, $string is splitted and returned as an array
     * 
     * @param string $string
     * @param array $patterns
     * @return array of splitted strings or array($string) if no split.
     */
    function splitCheck($string, $patterns) {
        $out_arr[] = $string;
        foreach ($patterns as $pattern) {
            if(strstr($string, $pattern)) {
                $arr = explode($pattern, $string);
                foreach($arr as $token) {
                    $out_arr[] = $token;
                }
            }
        }
        return $out_arr;
    }
    
    /**
     * Caching of similar artists to the input artist
     *
     * @param string $artist
     */
    function cacheSimilarArtists($artist) {
        $artist = Lastfm::wsEncode($artist);
        $url = "http://ws.audioscrobbler.com/1.0/artist/".$artist."/similar.txt";
        Info::add("Try to cache: ".$url);
        $data = file_get_contents($url);
        
        if($data == false) {
        	Info::add("Data retrieval from last.fm failed; " . $url);
        	return false;
        }
        //NOT IMPLEMENTED check if not "No artist exist"
        //preg_match("/No artist exists with this name:/.*", $data);
        
        
        $file = "./cache/".$artist;
        $fp = @fopen($file, "w");
        if ($fp) {
           $result = fwrite($fp, $data);
           fclose($fp);
        } else {
            trigger_error("Unable to open file for writing: ".$file, E_USER_ERROR);
            return false;
        }
        Info::add($result." bytes written"); 
        return true;
    }
    
    function cacheUser($username) {
        $url = "http://ws.audioscrobbler.com/1.0/user/".$username."/topartists.txt";
        Info::add("Try to cache: ".$url);
        $data = file_get_contents($url);
        $file = "./cache/users/".$username;
        $fp = @fopen($file, "w");
        if ($fp) {
           $result = fwrite($fp, $data);
           fclose($fp);
        } else {
            trigger_error("Unable to open file for writing: ".$file, E_USER_ERROR);
        }
        Info::add($result." bytes written"); 
    }
    
    function wsEncode($string) {
        //return urlencode(str_replace(array("%", "&", "'", "+"), array("%25", "%26","%27", "%2B"), utf8_encode(urldecode($string))));
        return urlencode(urlencode(urldecode($string)));
    }
    
    function naiveNameReplace($string) {
        if(strstr($string, "&")) {
            return str_replace("&", "and", $string);
        } elseif(substr($string, -1) == ".") {
            return substr($string, 0, -1);
        }
        return $string;
    }
    
    function saveArtistAvatar($artist) {
        $image = "./cache/images/".md5($artist).".jpg";
        if(fopen($image, "r")) {
            return $image;
        } 
        $src = ArtistWiki::getArtistMetadataXML($artist);
        $simpl = new IsterXmlSimpleXMLImpl();
        $doc = $simpl->load_string($src);
        if(empty($doc->methodResponse->params->param->value->struct->member[6]->value->string)) {
            trigger_error("Artist ".$artist." has no avatar?", E_USER_WARNING);
            $urlImg = "./cache/images/na.jpg";
        } else {
	        $urlImg = $doc->methodResponse->params->param->value->struct->member[6]->value->string->CDATA();
	        $tempArr = array_reverse(explode("/", $urlImg));
	        $imgName = $tempArr[0];
        }
        //$image = "./cache/images/".$imgName;
        Info::add("Transforming avatar for artist ".$artist.": ".$image);
        $image_transform =& Image_Transform::factory('');
		$image_transform->load($urlImg);
		$image_transform->resize(50,50);
		//$image_transform->scaleMaxLength('50');
		$image_transform->save($image,'jpg');
		if($image_transform->isError()){
			trigger_error($image_transform->getMessage(), E_USER_ERROR);
		}
        return $image;
    }
}
?>